package com.chagee.store.operation.server.generator.codeGenerator;


import com.chagee.store.operation.common.exception.BizErrorCode;
import com.chagee.store.operation.common.exception.BizException;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.Duration;
import java.util.List;
import java.util.Optional;

/**
 * @author: chang.jiawei
 * @date: 2024/01/15
 */
@Slf4j
public abstract class AbstractCodeGenerator {

	/**
	 * 生成一个特定规则编号
	 *
	 * @return 编号
	 */
	public String generate() {
		List<String> results = this.generate(NumberUtils.INTEGER_ONE);
		return results.stream().findFirst().orElseThrow(()->new BizException(BizErrorCode.CODE_GENERATE_FAIL));
	}

	/**
	 * 生成特定数量的特定规则序列号
	 *
	 * @return 编号
	 */
	public abstract List<String> generate(int quantity);

	/**
	 * 拼接序列号占位
	 *
	 * @param num 序列号数字 e.g. 1
	 * @return e.g. 001
	 */
	protected abstract String fillSerial(long num);

	/**
	 * 生成序列号
	 *
	 * @param redisTemplate redis数据
	 * @param redisKey      redisKey
	 * @param expireTime    过期时间
	 * @param quantity      生成的数量
	 * @return 序列号
	 */
	public List<String> generateSerial(RedisTemplate<String, Object> redisTemplate, String redisKey, Duration expireTime, int quantity) {
		log.info("item redis key:" + redisKey);
		String currentNumStr = (String)redisTemplate.opsForValue().get(redisKey);
		long current = NumberUtils.LONG_ZERO;
		if (StringUtils.isBlank(currentNumStr)) {
			redisTemplate.boundValueOps(redisKey).set(current, expireTime);
		} else {
			current = Long.parseLong(currentNumStr);
		}
		log.info("current={}", current);
		Long end = redisTemplate.opsForValue().increment(redisKey, quantity);
		Optional.ofNullable(end).orElseThrow(() -> new RuntimeException("生成序列号失败"));
		long start = end - quantity + 1;
		List<String> numbers = Lists.newArrayListWithCapacity(quantity);
		for (long num = start; num <= end; num++) {
			numbers.add(fillSerial(num));
		}
		return numbers;
	}

}
